
#include "AsynLogging.hpp"

namespace tulun
{
    const size_t BufMaxLen = 1024 * 4; // 4k
    const size_t BufQueueSize = 16;

    // class AsynLogging::
    // const int flushInterval_;
    // const std::string basename_;
    // const size_t rollSize_;
    //  std::atomic<bool> running_;

    // std::unique_ptr<std::thread> pthread_;
    // std::mutex mutex_;
    // std::condition_variable cond_;
    // std::string currenBuffer_;
    // std::vector<std::string> buffers_;
    // tulun::LogFile output_;

    void AsynLogging::workthreadfunc()
    {
        std::vector<std::string> bufferToWrite;
        countLatch_.countDown();
        while (running_)
        {
            {
                std::unique_lock<std::mutex> locker(mutex_);
                while (buffers_.empty() && running_)
                {
                    cond_.wait_for(locker, std::chrono::seconds(1));
                }
                buffers_.push_back(std::move(currenBuffer_));
                buffers_.swap(bufferToWrite);
                currenBuffer_.reserve(BufMaxLen); // 4k
                buffers_.resize(BufQueueSize);    // 16
            }
            if (bufferToWrite.size() > 5)
            {
                fprintf(stderr, "Dropped log message at large buffers %ld \n", bufferToWrite.size());
                bufferToWrite.erase(bufferToWrite.begin() + 2, bufferToWrite.end());
            }
            for (const auto &buf : bufferToWrite)
            {
                output_.append(buf);
            }
            bufferToWrite.clear();
        }
        output_.flush();
    }

    AsynLogging::AsynLogging(const std::string basename,
                             const size_t rollSize,
                             int flushInterval)
        : flushInterval_(flushInterval),
          rollSize_(rollSize),
          basename_(basename),
          running_{false},
          output_{basename, rollSize},
          countLatch_(1)
    {
        currenBuffer_.reserve(BufMaxLen);
        buffers_.resize(BufQueueSize);
    }
    AsynLogging::~AsynLogging()
    {
        if (running_)
        {
            stop();
        }
    }

    void AsynLogging::append(const std::string &msg) // C++
    {
        append(msg.c_str(), msg.size());
    }
    void AsynLogging::append(const char *msg, size_t len) // C
    {
        std::unique_lock<std::mutex> locker(mutex_);
        if (currenBuffer_.size() >= BufMaxLen ||
            currenBuffer_.capacity() - currenBuffer_.size() < len)
        {
            buffers_.push_back(std::move(currenBuffer_));
            currenBuffer_.reserve(BufMaxLen);
        }
        currenBuffer_.append(msg, len);
        cond_.notify_all();
    }
    void AsynLogging::start()
    {
        running_ = true;
        pthread_.reset(new std::thread(&AsynLogging::workthreadfunc, this));
        countLatch_.wait();
    }
    void AsynLogging::stop()
    {
        running_ = false;
        cond_.notify_all();
        pthread_->join();
    }
    void AsynLogging::flush()
    {
        std::vector<std::string> bufferToWrite;
        std::unique_lock<std::mutex> locker(mutex_);
        buffers_.push_back(std::move(currenBuffer_));
        buffers_.swap(bufferToWrite);
        currenBuffer_.reserve(BufMaxLen);
        buffers_.resize(BufQueueSize);

        for (const auto &buf : bufferToWrite)
        {
            output_.append(buf);
        }
        output_.flush();
        bufferToWrite.clear();
    }
} // namespace tulun